﻿using System.ComponentModel;
using System.Runtime.Serialization;
using BaseTypesLib.UiTypeEditors;
using System.Drawing.Design;
using BaseTypesLib.Model.Fonts;
using BaseTypesLib.Model.Backgrounds;
using System.Xml.Serialization;
using System.Reflection;
using System;
using System.Xml.Linq;
using System.Linq;
using System.Windows;
using Microsoft.Practices.Unity;

namespace BaseTypesLib.Model
{
    public enum FieldVerticalAlignment { Top, Center, Bottom }
    public enum FieldHorizontalAlignment { Left, Center, Right }
    public enum FieldVisibility { Public, Private, Protected }


    /// <summary>
    /// This class will be the base class for all UI elements added to a screen
    /// </summary>

    public abstract class Field:INotifyPropertyChanged
    {
        #region fields
        private string name;//the name of the field to use in java code
        private bool generateMember;//generate a class variable for the field?
        private FieldVisibility visibility;//the member visibility

        private bool focusable;//is the field focusable?
        private Manager manager;//field manager
        private FieldHorizontalAlignment hAlign;//alignment
        private FieldVerticalAlignment vAlign;//alignment

        private bool isSelected;//is the field selected? for the designer

        private Background background;
        private Font font;
        private Thickness margin;
        #endregion


        #region code generation properties

        [Category("Code generation")]
        [Description("The name of the field in the generated class")]
        public string Name
        {
            get { return name; }
            set
            {
                if (name != value)
                {
                    name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        
        [Category("Code generation")]
        [Description("True if you want a member to be generated for this field")]
        public bool GenerateMember
        {
            get { return generateMember; }
            set
            {
                if (generateMember != value)
                {
                    generateMember = value;
                    OnPropertyChanged("GenerateMember");
                }
            }
        }

        
        [Category("Code generation")]
        [Description("The member visibility inside the generated class")]
        public FieldVisibility Visibility
        {
            get { return visibility; }
            set
            {
                if (visibility != value)
                {
                    visibility = value;
                    OnPropertyChanged("Visibility");
                }
            }
        }

        #endregion

        #region properties
        [Category("Layout")]
        public Thickness Margin
        {
            get { return margin; }
            set
            {
                if (margin != value)
                {
                    margin = value;
                    OnPropertyChanged("Margin");
                }
            }
        }
        [Category("Appearance")]
        public Background Background
        {
            get { return background; }
            set
            {
                if (background != value)
                {
                    background = value;
                    OnPropertyChanged("Background");
                }
            }
        }
        [Category("Appearance")]
        public Font Font
        {
            get { return font; }
            set
            {
                if (font != value)
                {
                    font = value;
                    OnPropertyChanged("Font");
                }
            }
        }
        [Category("Appearance")]
        public bool Focusable
        {
            get { return focusable; }
            set
            {
                if (focusable != value)
                {
                    focusable = value;
                    OnPropertyChanged("Focusable");
                }
            }
        }
        [Browsable(false)]
        public Manager Manager
        {
            get { return manager; }
            set
            {
                if (manager != value)
                {
                    manager = value;
                    OnPropertyChanged("Manager");
                }
            }
        }
        [Category("Layout")]
        public FieldHorizontalAlignment HorizontalAlignment
        {
            get { return hAlign; }
            set
            {
                if (hAlign != value)
                {
                    hAlign = value;
                    OnPropertyChanged("HorizontalAlignment");
                }
            }
        }
        [Category("Layout")]
        public FieldVerticalAlignment VerticalAlignment
        {
            get { return vAlign; }
            set
            {
                if (vAlign != value)
                {
                    vAlign = value;
                    OnPropertyChanged("VerticalAlignment");
                }
            }
        }
        #endregion

        #region designer properties
        [Browsable(false)]
        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                if (isSelected != value)
                {
                    isSelected = value;
                    OnPropertyChanged("IsSelected");
                }
            }
        }
        #endregion

        public Field()
        {
            HorizontalAlignment = FieldHorizontalAlignment.Left;
            VerticalAlignment = FieldVerticalAlignment.Top;
            Focusable = true;
            GenerateMember = true;
            Margin = new Thickness();//new FieldMargin();
        }

        #region inpc
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
        #endregion

        public override string ToString()
        {
            return GetType().Name;
        }
        protected virtual XElement GetXmlElement()
        {
            XElement elem = new XElement(GetType().Name);
            if (HorizontalAlignment != FieldHorizontalAlignment.Left)
                elem.SetAttributeValue("HorizontalAlignment", HorizontalAlignment);
            if (VerticalAlignment != FieldVerticalAlignment.Top)
                elem.SetAttributeValue("VerticalAlignment", VerticalAlignment);
            elem.SetAttributeValue("GenerateMember", GenerateMember);
            elem.SetAttributeValue("Visibility", Visibility);
            if (Name != null && Name.Trim().Length > 0)
                elem.SetAttributeValue("Name", Name);
            elem.SetAttributeValue("Focusable", Focusable);

            if (Font != null)
            {
                elem.Add(Font.GetXml());
            }

            if (Background != null)
            {
                XElement bg = new XElement("Background");
                bg.Add(Background.GetXml());
                elem.Add(bg);
            }

            if(Margin!=null && Margin!=new Thickness() )
            {
                elem.SetAttributeValue("Margin", string.Format("{0}, {1}, {2}, {3}", Margin.Top, Margin.Right, Margin.Bottom, Margin.Left));
            }

            return elem;
        }
        protected virtual void FromXmlElement(XElement elem)
        {
            var ha = elem.Attributes("HorizontalAlignment").FirstOrDefault();
            if (ha != null)
                HorizontalAlignment = (FieldHorizontalAlignment)Enum.Parse(typeof(FieldHorizontalAlignment), ha.Value);
            else
                HorizontalAlignment = FieldHorizontalAlignment.Left;

            var va = elem.Attributes("VerticalAlignment").FirstOrDefault();
            if (va != null)
                VerticalAlignment = (FieldVerticalAlignment)Enum.Parse(typeof(FieldVerticalAlignment), va.Value);
            else
                VerticalAlignment = FieldVerticalAlignment.Top;


            var focusable = elem.Attributes("Focusable").FirstOrDefault();
            if (focusable != null)
                Focusable = (bool)focusable;
            var gen = elem.Attributes("GenerateMember").FirstOrDefault();
            if (gen != null)
                GenerateMember = (bool)gen;
            else
                GenerateMember = true;
            var name = elem.Attributes("Name").FirstOrDefault();
            if (name != null)
                Name = name.Value;
            var vis = elem.Attributes("Visibility").FirstOrDefault();
            if (vis != null)
                Visibility = (FieldVisibility)Enum.Parse(typeof(FieldVisibility), vis.Value);
            else
                Visibility = FieldVisibility.Private;

            var margins = elem.Attributes("Margin").FirstOrDefault();
            if (margins != null)
            {
                string[] ms = margins.Value.Split(',');
                if (ms.Length == 4)
                {
                    
                    margin.Top = int.Parse(ms[0].Trim());
                    margin.Right = int.Parse(ms[1].Trim());
                    margin.Bottom = int.Parse(ms[2].Trim());
                    margin.Left = int.Parse(ms[3].Trim());
                }
            }

            var font = elem.Elements("Font").FirstOrDefault();
            if (font != null)
            {
                Font = Font.FromXml(font);
            }
            var bg = elem.Elements("Background").FirstOrDefault();
            if (bg != null && bg.Elements().FirstOrDefault() != null)
            {
                Background = Background.FromXml(bg.Elements().First());
            }
        }
        public XElement GetXml()
        {
            return GetXmlElement();
        }
        public static Field FromXml(XElement elem)
        {
            //use xmlInclude to instantiate the types
            Field field = GetFieldFromName(elem.Name.LocalName);
            field.FromXmlElement(elem);
            return field;
        }
        private static Field GetFieldFromName(string name)
        {
            Assembly[] referencedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            var types = referencedAssemblies.SelectMany(p => p.GetTypes())
                .Where(p => p.IsSubclassOf(typeof(Field)) && !p.IsAbstract && p.Name==name)
                .ToList();

            if (types.Count == 1)
            {
                ConstructorInfo ci = types[0].GetConstructor(Type.EmptyTypes);
                return ci.Invoke(null) as Field;
            }
            throw new Exception("Multiple types with the same name were detected");
            
        }
    }
}
